import os
import subprocess
import gzip
import shutil
from datetime import datetime
from django.conf import settings
from django.utils import timezone
from .models import DatabaseConnection, DatabaseBackup, DatabaseAlert


class DatabaseBackupManager:
    """数据库备份管理器"""
    
    def __init__(self):
        # 创建备份目录
        self.backup_dir = os.path.join(settings.BASE_DIR, 'backups')
        if not os.path.exists(self.backup_dir):
            os.makedirs(self.backup_dir)
    
    def create_backup(self, db_conn, backup_name=None, backup_type='full', user=None):
        """创建数据库备份"""
        if not backup_name:
            timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
            backup_name = f"{db_conn.name}_{backup_type}_{timestamp}"
        
        # 创建备份记录
        backup_record = DatabaseBackup.objects.create(
            connection=db_conn,
            name=backup_name,
            file_path='',  # 稍后更新
            file_size=0,   # 稍后更新
            backup_type=backup_type,
            status='running',
            created_by=user
        )
        
        try:
            if db_conn.engine == 'mysql':
                file_path = self._backup_mysql(db_conn, backup_name)
            elif db_conn.engine == 'postgresql':
                file_path = self._backup_postgresql(db_conn, backup_name)
            elif db_conn.engine == 'sqlite3':
                file_path = self._backup_sqlite(db_conn, backup_name)
            else:
                raise ValueError(f"不支持的数据库类型: {db_conn.engine}")
            
            # 获取文件大小
            file_size = os.path.getsize(file_path)
            
            # 更新备份记录
            backup_record.file_path = file_path
            backup_record.file_size = file_size
            backup_record.status = 'completed'
            backup_record.completed_at = timezone.now()
            backup_record.save()
            
            return {
                'success': True,
                'backup_id': backup_record.id,
                'file_path': file_path,
                'file_size': file_size,
                'message': '备份创建成功'
            }
            
        except Exception as e:
            # 更新备份记录为失败状态
            backup_record.status = 'failed'
            backup_record.error_message = str(e)
            backup_record.completed_at = timezone.now()
            backup_record.save()
            
            # 创建备份失败告警
            DatabaseAlert.objects.create(
                connection=db_conn,
                alert_type='backup_failed',
                severity='high',
                message=f'数据库备份失败: {str(e)}'
            )
            
            return {
                'success': False,
                'backup_id': backup_record.id,
                'error': str(e),
                'message': f'备份创建失败: {str(e)}'
            }
    
    def _backup_mysql(self, db_conn, backup_name):
        """备份MySQL数据库"""
        file_path = os.path.join(self.backup_dir, f"{backup_name}.sql.gz")
        
        # 构建mysqldump命令
        cmd = [
            'mysqldump',
            f'--host={db_conn.host}',
            f'--port={db_conn.port}',
            f'--user={db_conn.username}',
            f'--password={db_conn.password}',
            '--single-transaction',
            '--routines',
            '--triggers',
            db_conn.database
        ]
        
        # 执行备份并压缩
        with gzip.open(file_path, 'wt') as f:
            process = subprocess.run(cmd, stdout=f, stderr=subprocess.PIPE, text=True)
            
            if process.returncode != 0:
                raise Exception(f"mysqldump失败: {process.stderr}")
        
        return file_path
    
    def _backup_postgresql(self, db_conn, backup_name):
        """备份PostgreSQL数据库"""
        file_path = os.path.join(self.backup_dir, f"{backup_name}.sql.gz")
        
        # 设置环境变量
        env = os.environ.copy()
        env['PGPASSWORD'] = db_conn.password
        
        # 构建pg_dump命令
        cmd = [
            'pg_dump',
            f'--host={db_conn.host}',
            f'--port={db_conn.port}',
            f'--username={db_conn.username}',
            '--no-password',
            '--verbose',
            '--clean',
            '--no-acl',
            '--no-owner',
            db_conn.database
        ]
        
        # 执行备份并压缩
        with gzip.open(file_path, 'wt') as f:
            process = subprocess.run(cmd, stdout=f, stderr=subprocess.PIPE, 
                                   text=True, env=env)
            
            if process.returncode != 0:
                raise Exception(f"pg_dump失败: {process.stderr}")
        
        return file_path
    
    def _backup_sqlite(self, db_conn, backup_name):
        """备份SQLite数据库"""
        file_path = os.path.join(self.backup_dir, f"{backup_name}.db.gz")
        
        # 直接复制SQLite文件并压缩
        if not os.path.exists(db_conn.database):
            raise Exception(f"SQLite数据库文件不存在: {db_conn.database}")
        
        with open(db_conn.database, 'rb') as f_in:
            with gzip.open(file_path, 'wb') as f_out:
                shutil.copyfileobj(f_in, f_out)
        
        return file_path
    
    def restore_backup(self, backup_id, target_db_conn=None):
        """恢复数据库备份"""
        try:
            backup = DatabaseBackup.objects.get(id=backup_id)
            
            if backup.status != 'completed':
                raise Exception("只能恢复已完成的备份")
            
            if not os.path.exists(backup.file_path):
                raise Exception("备份文件不存在")
            
            # 如果没有指定目标数据库，使用原数据库
            if not target_db_conn:
                target_db_conn = backup.connection
            
            if backup.connection.engine == 'mysql':
                self._restore_mysql(backup, target_db_conn)
            elif backup.connection.engine == 'postgresql':
                self._restore_postgresql(backup, target_db_conn)
            elif backup.connection.engine == 'sqlite3':
                self._restore_sqlite(backup, target_db_conn)
            else:
                raise ValueError(f"不支持的数据库类型: {backup.connection.engine}")
            
            return {
                'success': True,
                'message': '数据库恢复成功'
            }
            
        except Exception as e:
            return {
                'success': False,
                'error': str(e),
                'message': f'数据库恢复失败: {str(e)}'
            }
    
    def _restore_mysql(self, backup, target_db_conn):
        """恢复MySQL数据库"""
        # 构建mysql命令
        cmd = [
            'mysql',
            f'--host={target_db_conn.host}',
            f'--port={target_db_conn.port}',
            f'--user={target_db_conn.username}',
            f'--password={target_db_conn.password}',
            target_db_conn.database
        ]
        
        # 解压并恢复
        with gzip.open(backup.file_path, 'rt') as f:
            process = subprocess.run(cmd, stdin=f, stderr=subprocess.PIPE, text=True)
            
            if process.returncode != 0:
                raise Exception(f"mysql恢复失败: {process.stderr}")
    
    def _restore_postgresql(self, backup, target_db_conn):
        """恢复PostgreSQL数据库"""
        # 设置环境变量
        env = os.environ.copy()
        env['PGPASSWORD'] = target_db_conn.password
        
        # 构建psql命令
        cmd = [
            'psql',
            f'--host={target_db_conn.host}',
            f'--port={target_db_conn.port}',
            f'--username={target_db_conn.username}',
            '--no-password',
            target_db_conn.database
        ]
        
        # 解压并恢复
        with gzip.open(backup.file_path, 'rt') as f:
            process = subprocess.run(cmd, stdin=f, stderr=subprocess.PIPE, 
                                   text=True, env=env)
            
            if process.returncode != 0:
                raise Exception(f"psql恢复失败: {process.stderr}")
    
    def _restore_sqlite(self, backup, target_db_conn):
        """恢复SQLite数据库"""
        # 解压并覆盖目标文件
        with gzip.open(backup.file_path, 'rb') as f_in:
            with open(target_db_conn.database, 'wb') as f_out:
                shutil.copyfileobj(f_in, f_out)
    
    def delete_backup(self, backup_id):
        """删除备份"""
        try:
            backup = DatabaseBackup.objects.get(id=backup_id)
            
            # 删除备份文件
            if os.path.exists(backup.file_path):
                os.remove(backup.file_path)
            
            # 删除备份记录
            backup.delete()
            
            return {
                'success': True,
                'message': '备份删除成功'
            }
            
        except Exception as e:
            return {
                'success': False,
                'error': str(e),
                'message': f'备份删除失败: {str(e)}'
            }
    
    def list_backups(self, db_conn=None):
        """列出备份"""
        queryset = DatabaseBackup.objects.all()
        
        if db_conn:
            queryset = queryset.filter(connection=db_conn)
        
        return queryset.order_by('-created_at')
    
    def cleanup_old_backups(self, days=30):
        """清理旧备份"""
        from datetime import timedelta
        
        cutoff_date = timezone.now() - timedelta(days=days)
        old_backups = DatabaseBackup.objects.filter(created_at__lt=cutoff_date)
        
        deleted_count = 0
        for backup in old_backups:
            result = self.delete_backup(backup.id)
            if result['success']:
                deleted_count += 1
        
        return {
            'success': True,
            'deleted_count': deleted_count,
            'message': f'清理了 {deleted_count} 个旧备份'
        }
